package com.zch.rpc.service.server;

import com.zch.rpc.register.register.ServiceDiscovery;
import com.zch.rpc.register.register.ServiceRegistry;
import com.zch.rpc.register.register.ZkClient;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 服务器端
 * @author QJM
 *
 */
public class RpcServer {
	private int port;

	public RpcServer(int port) {
		this.port = port;
	}


	/**
	 * 启动rpc服务
	 * @param scanPackage 服务实现类所在包名，多个用英文逗号隔开
	 */
	public void start( String scanPackage) {
		ServerSocket server = null;
		try {
			// 1. 创建socket连接
			server = new ServerSocket(port);
			// 2. 获取所有rpc服务类，即发布服务（此map类似一个spring的ioc容器,我们此处的map key是接口名，value是实现类的实例对象）
			Map<String, Object> services = getService(scanPackage);
			System.out.println("服务成功已注册到 "+port+"端口");
			// 3. 创建线程池
			Executor threadPoolService = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
			while(true){
				// 4. 创建客户端连接（这一步依然是阻塞的，底层决定的）
				Socket client = server.accept();
				// 5. 查找并执行服务
				ServiceTask service = new ServiceTask(client, services);
				/**
				 * 6.利用多线程来做伪异步操作（原生的socket是BIO阻塞式运行，即多个请求过来的时候会排着队，一个请求处理完下一个才会接着处理，无法支撑高并发）
				 *   及时用多线程来解决，效率依然不会提升很多，多线程是很耗系统资源的，这块可以用nio(多路复用，底层用的是reactor)或者aio来提升效率
				 */

				threadPoolService.execute(service);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{//只有捕捉到异常的时候才会关闭监听，否则会一直循环运行不关闭
			//关闭监听
			if(server != null)
				try {
					server.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}

	/**
	 * 启动rpc服务
	 * @param scanPackage 服务实现类所在包名，多个用英文逗号隔开
	 * @param serviceRegistry 利用zookeeper服务注册
	 */
	public void start( String scanPackage, ServiceRegistry serviceRegistry) {
		ServerSocket server = null;
		try {
			Executor threadPoolService = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
			// 1. 创建socket连接
			server = new ServerSocket(port);
			// 2. 获取所有rpc服务类，即发布服务（此map类似一个spring的ioc容器,我们此处的map key是接口名，value是实现类的实例对象）
			Map<String, Object> services = getService(scanPackage);
			if(serviceRegistry!=null){
				/*ZkClient zkClient=new ZkClient(serviceRegistry.getHost(),serviceRegistry.getPort());
				serviceRegistry.setZkClient(zkClient);*/
				services.forEach((k,v)->{
					try {
						serviceRegistry.register(k,"localhost:"+port);
					} catch (IOException e) {
						e.printStackTrace();
					}
				});

			}

			System.out.println("服务成功已注册到zookeeper "+serviceRegistry.getPort()+"端口");
			// 3. 创建线程池
			while(true){
				// 4. 创建客户端连接（这一步依然是阻塞的，底层决定的）
				Socket client = server.accept();
				// 5. 查找并执行服务
				ServiceTask service = new ServiceTask(client, services);
				/**
				 * 6.利用多线程来做伪异步操作（原生的socket是BIO阻塞式运行，即多个请求过来的时候会排着队，一个请求处理完下一个才会接着处理，无法支撑高并发）
				 *   及时用多线程来解决，效率依然不会提升很多，多线程是很耗系统资源的，这块可以用nio(多路复用，底层用的是reactor)跟aio来提升效率
				 */

				threadPoolService.execute(service);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}finally{//只有捕捉到异常的时候才会关闭监听，否则会一直循环运行不关闭
			//关闭监听
			if(server != null)
				try {
					server.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}


	/**
	 * 实例化所有rpc服务的实现类（类似于spring的扫描，在这个包下，凡是有RpcService注解的的类都会被实例化）
	 * @param scanPackage 服务实现类所在包名，多个用英文逗号隔开
	 * @return
	 */
	public Map<String,Object> getService(String scanPackage){
		try {
			Map<String, Object> services = new HashMap<String, Object>();
			//扫描包数组
			String[] packages = scanPackage.split(",");
			//待实例化的服务实现类
			List<Class<?>> classes = new ArrayList<Class<?>>();
			for(String basePackage:packages){
				List<Class<?>> classList = getClasses(basePackage);
				classes.addAll(classList);
			}
			//循环实例化
			for(Class<?> cla:classes){
				Object obj = cla.newInstance();
				services.put(cla.getAnnotation(com.zch.rpc.client.annotation.RpcService .class).value().getName(), obj);
			}
			return services;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 获取包下所有有@RpcSercive注解的类
	 * @param pckgname
	 * @return
	 * @throws ClassNotFoundException
	 */
	public static List<Class<?>> getClasses(String pckgname) throws ClassNotFoundException {
		List<Class<?>> classes = new ArrayList<Class<?>>();
		File directory = null;
		try {
			ClassLoader cld = Thread.currentThread().getContextClassLoader();
			if (cld == null)
				throw new ClassNotFoundException("Can't get class loader.");
			String path = pckgname.replace('.', '/');
			URL resource = cld.getResource(path);
			if (resource == null)
				throw new ClassNotFoundException("No resource for " + path);
			directory = new File(resource.getFile());
		} catch (NullPointerException x) {
			throw new ClassNotFoundException(pckgname + " (" + directory + ") does not appear to be a valid package a");
		}
		if (directory.exists()) {
			//获取所有文件
			String[] files = directory.list();
			File[] fileList = directory.listFiles();
			for (int i = 0;fileList != null && i < fileList.length; i++) {
				File file = fileList[i];
				//判断是否是Class文件
				if (file.isFile() && file.getName().endsWith(".class")) {
					Class<?> clazz = Class.forName(pckgname + '.' + files[i].substring(0, files[i].length() - 6));
					if(clazz.getAnnotation(com.zch.rpc.client.annotation.RpcService .class) != null){
						classes.add(clazz);
					}
				}else if(file.isDirectory()){ //如果是目录，递归查找
					List<Class<?>> result = getClasses(pckgname+"."+file.getName());
					if(result != null && result.size() != 0){
						classes.addAll(result);
					}
				}
			}
		} else{
			throw new ClassNotFoundException(pckgname + " does not appear to be a valid package b");
		}
		return classes;
	}
}
